/******************************************************************************
* 	Copyright (c) 2007-2008 All rights reserved
*		Asemantics S.r.l
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer. 
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment:
*       "This product includes software developed by 
*	 Asemantics S.r.l."
*    Alternately, this acknowledgment may appear in the software itself,
*    if and wherever such third-party acknowledgments normally appear.
*
* 4. All advertising materials mentioning features or use of this software
*    must display the following acknowledgement:
*    This product includes software developed by Asemantics S.r.l.
*    the Semantic Web company, Rome, London, Leiden and its contributors. 
*
* 5. Neither the name of the company nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Andrea Marchesini - baku@asemantics.com
*
******************************************************************************/

#ifndef HAVE_CONFIG_H
#  error config.h ?!?
#endif
#include <config.h>

#include <log2html.h>
#include <html.h>

#include <string.h>
#include <errno.h>

#include "pages.h"

static gchar *html_title (gchar * string, gchar * b);
static gchar *html_double2date (gdouble date);
static gchar *html_escape (gchar * input);

/* INIT *******************************************************************/
static gchar *html_init_substring (gchar * string);
static gchar *html_init_substring_real (gchar * string, gchar * a, gchar * b);

void
html_init (struct data_t *data)
{
  gchar **split;

  split = g_strsplit (log2html_html, "CONTENT", 2);

  data->html_header = html_init_substring (split[0]);
  data->html_footer = html_init_substring (split[1]);

  g_strfreev (split);
}

static gchar *
html_init_substring (gchar * string)
{
  gchar *tmp;
  gchar *footer;
  time_t t;

  tmp =
    html_init_substring_real (string, "PACKAGE",
			      "TheBox Controller Log2Html");

  string = html_init_substring_real (tmp, "VERSION", VERSION);

  t = time (NULL);
  footer =
    g_strdup_printf ("Date: %s - generated by TheBox Controller Log2Html",
		     ctime (&t));
  string = html_init_substring_real (tmp, "FOOTER", footer);
  g_free (footer);

  g_free (tmp);

  return string;
}

static gchar *
html_init_substring_real (gchar * string, gchar * a, gchar * b)
{
  gchar **split;
  GString *str;
  gint i;

  if (!(split = g_strsplit (string, a, -1)))
    return g_strdup ("");

  if (!split[0])
    {
      g_strfreev (split);
      return g_strdup ("");
    }

  str = g_string_new (split[0]);

  for (i = 1; split[i]; i++)
    {
      str = g_string_append (str, b);
      str = g_string_append (str, split[i]);
    }

  g_strfreev (split);
  return g_string_free (str, FALSE);
}

static gchar *
html_title (gchar * string, gchar * b)
{
  return html_init_substring_real (string, "TITLE", b);
}

/* LOG ***************************************************************/

static gboolean html_log_index (struct data_t *data, struct log_t *log,
				gchar * basename, GError ** error);
static gboolean html_log_dot (struct data_t *data, struct log_t *log,
			      gchar * basename, GError ** error);
static gboolean html_log_istance (struct data_t *data, struct log_t *log,
				  gint pid, gchar * basename,
				  GError ** error);

gboolean
html_log (struct data_t *data, struct log_t *log, gchar * basename,
	  GError ** error)
{
  GList *list;
  gint pid = 0;

  if (html_log_index (data, log, basename, error) == FALSE)
    return FALSE;

  if (html_log_dot (data, log, basename, error) == FALSE)
    return FALSE;

  /* Analyze any istance and write the node: */
  for (list = log->istances; list; list = list->next)
    {
      struct log_istance_t *istance = list->data;

      if (!pid || pid != istance->pid)
	{
	  if (pid
	      && html_log_istance (data, log, pid, basename, error) == FALSE)
	    return FALSE;

	  pid = istance->pid;
	}
    }

  if (pid && html_log_istance (data, log, pid, basename, error) == FALSE)
    return FALSE;

  return TRUE;
}

static gboolean
html_log_index (struct data_t *data, struct log_t *log, gchar * basename,
		GError ** error)
{
  FILE *fl;

  gchar *tmp;
  gchar *filename;

  tmp = g_strdup_printf ("%s.html", basename);
  filename = g_build_path (G_DIR_SEPARATOR_S, data->directory, tmp, NULL);
  g_free (tmp);

  if ((fl = g_fopen (filename, "w")) < 0)
    {
      g_set_error (error, log2html_error_quark (), 0,
		   "Error opening the file '%s': %s", filename,
		   strerror (errno));
      return FALSE;
    }

  tmp = html_title (data->html_header, "Controller - Log2Html");
  g_fprintf (fl, "%s\n", tmp);
  g_free (tmp);

  g_fprintf (fl, "<table>\n");
  g_fprintf (fl, "<th>Startup</th>\n");
  g_fprintf (fl, "<th>Quit</th>\n");
  g_fprintf (fl, "<th>Pid</th>\n");
  g_fprintf (fl, "<th># Logs</th>\n");
  g_fprintf (fl, "<th># Waiting istances</th>\n");

  g_fprintf (fl, "<tr>\n");
  g_fprintf (fl, "<td>%s</td>\n", html_double2date (log->startup));
  g_fprintf (fl, "<td>%s</td>\n", html_double2date (log->quit));
  g_fprintf (fl, "<td>%d</td>\n", log->pid);
  g_fprintf (fl, "<td>%d</td>\n", g_list_length (log->istances));
  g_fprintf (fl, "<td>%d</td>\n", g_list_length (log->wait_istances));
  g_fprintf (fl, "</tr>\n");

  g_fprintf (fl, "</table>\n");

  g_fprintf (fl, "<div class=\"image\">");
  g_fprintf (fl,
	     "<a href=\"%s_dot.html\"><img src=\"%s.png\" alt=\"Map for the task\" /></a>",
	     basename, basename);
  g_fprintf (fl, "</div>\n");

  g_fprintf (fl, "%s\n", data->html_footer);

  fclose (fl);
  return TRUE;
}

static gboolean
html_log_dot (struct data_t *data, struct log_t *log, gchar * basename,
	      GError ** error)
{
  FILE *fl;

  gchar *tmp;
  gchar *filename;

  tmp = g_strdup_printf ("%s_dot.html", basename);
  filename = g_build_path (G_DIR_SEPARATOR_S, data->directory, tmp, NULL);
  g_free (tmp);

  if ((fl = g_fopen (filename, "w")) < 0)
    {
      g_set_error (error, log2html_error_quark (), 0,
		   "Error opening the file '%s': %s", filename,
		   strerror (errno));
      return FALSE;
    }

  tmp = html_title (data->html_header, "Controller - Log2Html");
  g_fprintf (fl, "%s\n", tmp);
  g_free (tmp);

  g_fprintf (fl, "<table>\n");
  g_fprintf (fl, "<th>Startup</th>\n");
  g_fprintf (fl, "<th>Quit</th>\n");
  g_fprintf (fl, "<th>Pid</th>\n");
  g_fprintf (fl, "<th># Logs</th>\n");
  g_fprintf (fl, "<th># Waiting istances</th>\n");

  g_fprintf (fl, "<tr>\n");
  g_fprintf (fl, "<td>%s</td>\n", html_double2date (log->startup));
  g_fprintf (fl, "<td>%s</td>\n", html_double2date (log->quit));
  g_fprintf (fl, "<td>%d</td>\n", log->pid);
  g_fprintf (fl, "<td>%d</td>\n", g_list_length (log->istances));
  g_fprintf (fl, "<td>%d</td>\n", g_list_length (log->wait_istances));
  g_fprintf (fl, "</tr>\n");

  g_fprintf (fl, "</table>\n");

  g_fprintf (fl, "<div class=\"dotimage\">");
  g_fprintf (fl, "%s", log->map);
  g_fprintf (fl,
	     "<img src=\"%s.png\" alt=\"Map for the task\" usemap=\"#log2html\" />",
	     basename);
  g_fprintf (fl, "</div>\n");

  g_fprintf (fl, "%s\n", data->html_footer);

  fclose (fl);
  return TRUE;
}

static gboolean html_log_istance_info (struct data_t *data,
				       struct log_istance_t *istance,
				       gint pid, gchar * basename, gint numb,
				       GError ** error);

static gboolean
html_log_istance (struct data_t *data, struct log_t *log, gint pid,
		  gchar * basename, GError ** error)
{
  FILE *fl;

  gchar *tmp, *tmp2;
  gchar *filename;

  gint numb = 0;

  GList *list;

  struct log_istance_t *istance;
  gboolean first = FALSE;

  tmp = g_strdup_printf ("%s_%d.html", basename, pid);
  filename = g_build_path (G_DIR_SEPARATOR_S, data->directory, tmp, NULL);
  g_free (tmp);

  if ((fl = g_fopen (filename, "w")) < 0)
    {
      g_set_error (error, log2html_error_quark (), 0,
		   "Error opening the file '%s': %s", filename,
		   strerror (errno));
      return FALSE;
    }

  for (list = log->istances; list; list = list->next)
    {
      istance = list->data;

      if (istance->pid == pid)
	{
	  if (first == FALSE)
	    {
	      tmp2 =
		g_strdup_printf ("Istance: %s (Pid: %d%s)", istance->process,
				 istance->pid,
				 istance->
				 process_meta ? " - meta process" : "");
	      tmp = html_title (data->html_header, tmp2);
	      g_free (tmp2);

	      g_fprintf (fl, "%s\n", tmp);
	      g_free (tmp);

	      g_fprintf (fl, "<table>\n");

	      first = TRUE;
	    }

	  g_fprintf (fl, "<tr>\n");
	  g_fprintf (fl, "<td>%s</td>\n", html_double2date (istance->time));

	  switch (istance->type)
	    {
	    case ISTANCE_BORN:
	      g_fprintf (fl,
			 "<td style=\"background:" ISTANCE_BORN_COLOR
			 ";\">BORN</td>\n");
	      break;

	    case ISTANCE_DIED:
	      g_fprintf (fl,
			 "<td style=\"background:" ISTANCE_DIED_COLOR
			 ";\">DIED</td>\n");
	      break;

	    case ISTANCE_WAIT:
	      g_fprintf (fl,
			 "<td style=\"background:" ISTANCE_WAIT_COLOR
			 ";\">WAIT</td>\n");
	      break;

	    case ISTANCE_ERROR_EXEC:
	      g_fprintf (fl,
			 "<td style=\"background:" ISTANCE_ERROR_COLOR
			 ";\">ERROR_EXEC</td>\n");
	      break;

	    case ISTANCE_ERROR_OUTPUT:
	      g_fprintf (fl,
			 "<td style=\"background:" ISTANCE_ERROR_COLOR
			 ";\">ERROR_OUTPUT</td>\n");
	      break;

	    case ISTANCE_ERROR_IO:
	      g_fprintf (fl,
			 "<td style=\"background:" ISTANCE_ERROR_COLOR
			 ";\">ERROR_IO</td>\n");
	      break;

	    case ISTANCE_ERROR_PROCESS:
	      g_fprintf (fl,
			 "<td style=\"background:" ISTANCE_ERROR_COLOR
			 ";\">ERROR_PROCESS</td>\n");
	      break;

	    case ISTANCE_ERROR_PARSER:
	      g_fprintf (fl,
			 "<td style=\"background:" ISTANCE_ERROR_COLOR
			 ";\">ERROR_PARSER</td>\n");
	      break;
	    }

	  g_fprintf (fl, "<td><a href=\"%s_%d_%d.html\">info</a></td>\n",
		     basename, pid, numb);

	  if (html_log_istance_info
	      (data, istance, pid, basename, numb, error) == FALSE)
	    return FALSE;

	  g_fprintf (fl, "</tr>\n");

	  numb++;
	}

      else if (first == TRUE)
	break;
    }

  g_fprintf (fl, "</table>\n");
  g_fprintf (fl, "%s\n", data->html_footer);

  fclose (fl);
  return TRUE;
}

static gboolean
html_log_istance_info (struct data_t *data, struct log_istance_t *istance,
		       gint pid, gchar * basename, gint numb, GError ** error)
{
  FILE *fl;

  gchar *tmp, *tmp2;
  gchar *filename;

  tmp = g_strdup_printf ("%s_%d_%d.html", basename, pid, numb);
  filename = g_build_path (G_DIR_SEPARATOR_S, data->directory, tmp, NULL);
  g_free (tmp);

  if ((fl = g_fopen (filename, "w")) < 0)
    {
      g_set_error (error, log2html_error_quark (), 0,
		   "Error opening the file '%s': %s", filename,
		   strerror (errno));
      return FALSE;
    }

  tmp2 =
    g_strdup_printf ("Istance: %s (Pid: %d%s) - info", istance->process,
		     istance->pid,
		     istance->process_meta ? " - meta process" : "");
  tmp = html_title (data->html_header, tmp2);
  g_free (tmp2);

  g_fprintf (fl, "%s\n", tmp);
  g_free (tmp);

  g_fprintf (fl, "<ul>\n");

  switch (istance->type)
    {
    case ISTANCE_BORN:
      g_fprintf (fl, "<li>Log Type: BORN</li>\n");

      tmp = html_escape (istance->born.input);
      g_fprintf (fl, "<li>Input:\n<pre>%s</pre></li>", tmp);
      g_free (tmp);

      g_fprintf (fl, "<li>Inline: %s</li>",
		 istance->born.inl == TRUE ? "true" : "false");

      if (istance->born.parent)
	g_fprintf (fl, "<li>Parent: <a href=\"%s_%d.html\">%d</a></li>",
		   basename, istance->born.parent, istance->born.parent);

      break;

    case ISTANCE_DIED:
      g_fprintf (fl, "<li>Log Type: DIED</li>\n");

      tmp = html_escape (istance->died.output);
      g_fprintf (fl, "<li>Output:\n<pre>%s</pre></li>", tmp);
      g_free (tmp);

      g_fprintf (fl, "<li>Status: %d</li>", istance->died.status);

      break;

    case ISTANCE_WAIT:
      g_fprintf (fl, "<li>Log Type: WAIT</li>\n");

      g_fprintf (fl, "<li>No log about this operation</li>\n");
      break;

    case ISTANCE_ERROR_EXEC:
      g_fprintf (fl, "<li>Log Type: ERROR EXEC</li>\n");

      tmp = html_escape (istance->error_exec.reason);
      g_fprintf (fl, "<li>Reason:\n<pre>%s</pre></li>", tmp);
      g_free (tmp);

      tmp = html_escape (istance->error_exec.command);
      g_fprintf (fl, "<li>Command:\n<pre>%s</pre></li>", tmp);
      g_free (tmp);
      break;

    case ISTANCE_ERROR_OUTPUT:
      g_fprintf (fl, "<li>Log Type: ERROR OUTPUT</li>\n");

      tmp = html_escape (istance->error_output);
      g_fprintf (fl, "<li>Error:\n<pre>%s</pre></li>", tmp);
      g_free (tmp);

      break;

    case ISTANCE_ERROR_IO:
      g_fprintf (fl, "<li>Log Type: ERROR IO</li>\n");

      tmp = html_escape (istance->error_io.operation);
      g_fprintf (fl, "<li>Operation:\n<pre>%s</pre></li>", tmp);
      g_free (tmp);

      tmp = html_escape (istance->error_io.error);
      g_fprintf (fl, "<li>Error:\n<pre>%s</pre></li>", tmp);
      g_free (tmp);
      break;

    case ISTANCE_ERROR_PROCESS:
      g_fprintf (fl, "<li>Log Type: ERROR PROCESS</li>\n");

      tmp = html_escape (istance->error_process.process);
      g_fprintf (fl, "<li>Process:\n<pre>%s</pre></li>", tmp);
      g_free (tmp);

      tmp = html_escape (istance->error_process.error);
      g_fprintf (fl, "<li>Error:\n<pre>%s</pre></li>", tmp);
      g_free (tmp);
      break;

    case ISTANCE_ERROR_PARSER:
      g_fprintf (fl, "<li>Log Type: ERROR PARSER</li>\n");

      tmp = html_escape (istance->error_parser);
      g_fprintf (fl, "<li>Error:\n<pre>%s</pre></li>", tmp);
      g_free (tmp);
      break;

    }

  g_fprintf (fl, "</ul>\n");

  g_fprintf (fl, "%s\n", data->html_footer);

  fclose (fl);
  return TRUE;
}

static gchar *
html_escape (gchar * str)
{
  gchar *ptr;
  GString *string;
  gunichar unichr;

  if (!strstr (str, "__TB_CONTROLLER_SPLITTER_IDENTIFIER__"))
    str = g_strdup (str);
  else
    {
      gchar **split;
      gint i;

      split = g_strsplit (str, "__TB_CONTROLLER_SPLITTER_IDENTIFIER__", -1);

      string = g_string_new (NULL);

      for (i = 0; split && split[i]; i++)
	{
	  string = g_string_append (string, "Multiple input: ");
	  string = g_string_append (string, split[i]);
	  string = g_string_append (string, "\n");
	}

      g_strfreev (split);

      str = g_string_free (string, FALSE);
    }

  string = g_string_new (NULL);
  ptr = str;

  while ((unichr = g_utf8_get_char (ptr)))
    {
      ptr = g_utf8_next_char (ptr);

      switch (unichr)
	{
	case '<':
	  string = g_string_append (string, "&lt;");
	  break;

	case '>':
	  string = g_string_append (string, "&gt;");
	  break;

	case '&':
	  string = g_string_append (string, "&amp;");
	  break;

	case '\'':
	  string = g_string_append (string, "&apos;");
	  break;

	case '\"':
	  string = g_string_append (string, "&quot;");
	  break;

	default:
	  string = g_string_append_unichar (string, unichr);
	  break;
	}
    }

  g_free (str);
  return g_string_free (string, FALSE);
}

/* INDEX ********************************************************************/
gboolean
html_index (struct data_t * data, GError ** error)
{
  FILE *fl;
  gchar *tmp;
  gchar *filename;

  GList *list;
  gint numb = 0;

  filename =
    g_build_path (G_DIR_SEPARATOR_S, data->directory, "index.html", NULL);

  if ((fl = g_fopen (filename, "w")) < 0)
    {
      g_set_error (error, log2html_error_quark (), 0,
		   "Error opening the file '%s': %s", filename,
		   strerror (errno));
      return FALSE;
    }

  tmp = html_title (data->html_header, "Controller - Log2Html");
  g_fprintf (fl, "%s\n", tmp);
  g_free (tmp);

  g_fprintf (fl, "<table>\n");
  g_fprintf (fl, "<th>ID</th>\n");
  g_fprintf (fl, "<th>Startup</th>\n");
  g_fprintf (fl, "<th>Quit</th>\n");
  g_fprintf (fl, "<th>Pid</th>\n");
  g_fprintf (fl, "<th># Logs</th>\n");
  g_fprintf (fl, "<th># Waiting istances</th>\n");

  for (list = data->logs; list; list = list->next)
    {
      struct log_t *log = list->data;

      g_fprintf (fl, "<tr>\n");
      g_fprintf (fl, "<td><a href=\"%d.html\">Task: %d</a></td>\n", numb,
		 numb);
      numb++;

      g_fprintf (fl, "<td>%s</td>\n", html_double2date (log->startup));
      g_fprintf (fl, "<td>%s</td>\n", html_double2date (log->quit));
      g_fprintf (fl, "<td>%d</td>\n", log->pid);
      g_fprintf (fl, "<td>%d</td>\n", g_list_length (log->istances));
      g_fprintf (fl, "<td>%d</td>\n", g_list_length (log->wait_istances));
      g_fprintf (fl, "</tr>\n");
    }

  g_fprintf (fl, "</table>\n");
  g_fprintf (fl, "%s\n", data->html_footer);

  fclose (fl);
  return TRUE;
}

static gchar *
html_double2date (gdouble date)
{
  static gchar buffer[1024];
  struct tm *tm;
  time_t tmp;

  tmp = (time_t) date;
  tm = localtime (&tmp);

  g_sprintf (buffer, "%.4d-%.2d-%.2d %.2d:%.2d.%d.%3.0f", tm->tm_year + 1900,
	     tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
	     (date - tmp) * 1000);

  return buffer;
}

/* EOF */
